package com.zrc.japktool.apk.merge;

import org.jf.baksmali.Baksmali;
import org.jf.baksmali.BaksmaliOptions;
import org.jf.dexlib2.DexFileFactory;
import org.jf.dexlib2.HiddenApiRestriction;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.builder.MutableMethodImplementation;
import org.jf.dexlib2.iface.*;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.formats.Instruction31i;
import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.iface.value.EncodedValue;
import org.jf.dexlib2.iface.value.IntEncodedValue;
import org.jf.dexlib2.iface.value.LongEncodedValue;
import org.jf.dexlib2.immutable.ImmutableClassDef;
import org.jf.dexlib2.immutable.ImmutableField;
import org.jf.dexlib2.immutable.ImmutableMethod;
import org.jf.dexlib2.immutable.ImmutableMethodImplementation;
import org.jf.dexlib2.immutable.instruction.ImmutableInstruction;
import org.jf.dexlib2.immutable.instruction.ImmutableInstruction31i;
import org.jf.dexlib2.immutable.instruction.ImmutableInstruction31t;
import org.jf.dexlib2.immutable.instruction.ImmutableInstruction35c;
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference;
import org.jf.dexlib2.immutable.value.ImmutableEncodedValue;
import org.jf.dexlib2.immutable.value.ImmutableEncodedValueFactory;
import org.jf.dexlib2.immutable.value.ImmutableIntEncodedValue;
import org.jf.smali.Main;

import javax.annotation.Nonnull;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class DexEditor {

    public static void dex2Smali(String dexFilePath, String outDirPath) throws Exception {
        File inDexFile = new File(dexFilePath);
        if (!(inDexFile.getName().endsWith(".dex") && inDexFile.exists())) {
            throw new Exception("dex文件路径错误...");
        }

        File outDirFile = new File(outDirPath);
        //if (!(outDirFile.isDirectory()&&inDexFile.exists())){
        //    throw new Exception("dex输出文件夹路径错误...");
        //}

        // 线程数，可根据CPU核心数调整
        int threadCount = Runtime.getRuntime().availableProcessors();

        try {
            // 加载dex文件
            DexFile dexFile = DexFileFactory.loadDexFile(inDexFile, null);

            // 配置baksmali选项
            BaksmaliOptions options = new BaksmaliOptions();
            // 注意：最新源码中outputDirectory可能已移至方法参数，而非options
            options.allowOdex = false; // 不处理odex文件
            options.deodex = false;    // 不需要反odex处理
            options.implicitReferences = false; // 显式引用

            // 执行反编译（dex转smali）
            // 使用最新的disassembleDexFile方法，传入输出目录、线程数和选项
            boolean success = Baksmali.disassembleDexFile(dexFile, outDirFile, threadCount, options);

            if (success) {
                System.out.println("Dex转换为smali成功，输出目录：" + outDirFile.getAbsolutePath());
            } else {
                System.err.println("Dex转换为smali失败");
            }
        } catch (IOException e) {
            System.err.println("转换失败：" + e.getMessage());
            e.printStackTrace();
            throw new Exception("转换失败：" + e.getMessage());
        } catch (RuntimeException e) {
            System.err.println("处理过程中发生错误：" + e.getMessage());
            e.printStackTrace();
            throw new Exception("处理过程中发生错误：" + e.getMessage());
        }
    }

    public static void smali2Dex(String smaliDir, String outDexFilePath) throws Exception {
        if (!outDexFilePath.endsWith(".dex")) {
            throw new Exception("dex输出文件路径错误...");
        }
        try {
            // 构建命令行参数，使用兼容的参数名称
            List<String> commandArgs = new ArrayList<>();
            commandArgs.add("assemble");  // 汇编命令
            commandArgs.add("-o");        // 输出文件参数
            commandArgs.add(outDexFilePath);
            commandArgs.add("-a");        // API级别参数（旧版本常用参数）
            commandArgs.add("28");        // API级别值
            commandArgs.add(smaliDir);    // smali文件目录

            // 转换为数组并执行
            String[] params = commandArgs.toArray(new String[0]);
            Main.main(params);
            System.out.println("Smali转换为Dex成功，输出文件：" + outDexFilePath);
        } catch (Exception e) {
            System.err.println("转换过程中发生错误：" + e.getMessage());
            e.printStackTrace();
            throw new Exception("转换过程中发生错误：" + e.getMessage());
        }
    }

    public static void replaceAndWrite(File file, Map<String, String> ids) throws Exception {
        // 参数校验
        if (file == null || !file.exists() || !file.isFile() || ids == null || ids.isEmpty()) {
            return;
        }

        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            // 读取文件内容
            byte[] buffer = new byte[(int) file.length()];
            fis = new FileInputStream(file);
            int bytesRead = fis.read(buffer);
            String content = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);

            // 替换内容
            for (Map.Entry<String, String> entry : ids.entrySet()) {
                content = content.replace(entry.getKey(), entry.getValue());
            }

            // 写入文件
            fos = new FileOutputStream(file);
            fos.write(content.getBytes(StandardCharsets.UTF_8));
            fos.flush();

        } catch (IOException e) {
            System.err.println("文件处理失败: " + file.getAbsolutePath() + ", 错误: " + e.getMessage());
            throw new Exception("文件处理失败: " + file.getAbsolutePath() + ", 错误: " + e.getMessage());
        } finally {

            try {
                if (fis != null) fis.close();
                if (fos != null) fos.close();
            } catch (IOException e) {
                System.err.println("关闭流失败: " + e.getMessage());
                throw new Exception("关闭流失败: " + e.getMessage());
            }
        }
    }

    public static void modifiyClassDefs(List<ClassDef> targetList, Map<String, String> ids) {
        List<ClassDef> modifiedClassDefs = new ArrayList<>(targetList.size());
        for (ClassDef classDef : targetList) {
            //System.out.println("---------------"+classDef.getType());
            modifiedClassDefs.add(modifySingleClassDef(classDef, ids));
        }
        targetList.clear();
        targetList.addAll(modifiedClassDefs);
    }

    private static ClassDef modifySingleClassDef(ClassDef originalClass, Map<String, String> ids) {
        // 分别处理直接方法和虚拟方法
        List<Method> modifiedDirectMethods = new ArrayList<>();
        for (Method method : originalClass.getDirectMethods()) {
            modifiedDirectMethods.add(modifySingleMethod(method, ids));
        }

        List<Method> modifiedVirtualMethods = new ArrayList<>();
        for (Method method : originalClass.getVirtualMethods()) {
            modifiedVirtualMethods.add(modifySingleMethod(method, ids));
        }

        // 创建新的类定义，使用正确的ImmutableClassDef构造方法
        return new ImmutableClassDef(
                originalClass.getType(),
                originalClass.getAccessFlags(),
                originalClass.getSuperclass(),
                originalClass.getInterfaces(),
                originalClass.getSourceFile(),
                originalClass.getAnnotations(),
                originalClass.getStaticFields(),
                originalClass.getInstanceFields(),
                modifiedDirectMethods,
                modifiedVirtualMethods
        );
    }

    private static Method modifySingleMethod(Method originalMethod, Map<String, String> ids) {
        MethodImplementation originalImpl = originalMethod.getImplementation();
        if (originalImpl == null) {
            return originalMethod; // 无实现的方法无需修改
        }

        // 处理方法指令
        List<Instruction> modifiedInstructions = new ArrayList<>();
        for (Instruction instr : originalImpl.getInstructions()) {
            modifiedInstructions.add(modifyInstruction(instr, ids));
        }

        // 创建新的方法实现（明确使用ImmutableMethodImplementation）
        ImmutableMethodImplementation modifiedImpl = new ImmutableMethodImplementation(
                originalImpl.getRegisterCount(),
                modifiedInstructions,
                originalImpl.getTryBlocks(),
                originalImpl.getDebugItems()
        );

        // 关键修正：使用getParameters()而非getParameterTypes()，传递MethodParameter对象集合
        // 完全匹配ImmutableMethod构造方法的参数要求
        return new ImmutableMethod(
                originalMethod.getDefiningClass(),
                originalMethod.getName(),
                originalMethod.getParameters(), // 传递完整的参数对象（包含类型和注解）
                originalMethod.getReturnType(),
                originalMethod.getAccessFlags(),
                originalMethod.getAnnotations(),
                originalMethod.getHiddenApiRestrictions(),
                modifiedImpl
        );
    }

    private static Instruction modifyInstruction(Instruction originalInstr, Map<String, String> ids) {
        // 处理31i格式的常量指令（如const v0, 0x7f0b001c）
        if (originalInstr instanceof Instruction31i) {
            Instruction31i constInstr = (Instruction31i) originalInstr;
            // 关键修正：使用getNarrowLiteral()获取32位常量值，而非getWideLiteral()
            int originalValue = constInstr.getNarrowLiteral();
            // 生成与smali中格式一致的key（如0x7f0b001c）
            String key = String.format("0x%08x", originalValue);
            if (ids.containsKey(key)) {
                // 解析新值并创建替换指令
                String newValueStr = ids.get(key);
                int newValue = Integer.decode(newValueStr);
                // 返回新的不可变指令
                return new ImmutableInstruction31i(
                        constInstr.getOpcode(),
                        constInstr.getRegisterA(),
                        newValue
                );
            }
        }

        // 处理31t格式的跳转指令（如果需要）
        if (originalInstr instanceof Instruction31t) {
            Instruction31t jumpInstr = (Instruction31t) originalInstr;
            int originalValue = jumpInstr.getCodeOffset();
            String key = String.format("0x%08x", originalValue);
            if (ids.containsKey(key)) {
                String newValueStr = ids.get(key);
                int newValue = Integer.decode(newValueStr);
                return new ImmutableInstruction31t(
                        jumpInstr.getOpcode(),
                        jumpInstr.getRegisterA(),
                        newValue
                );
            }
        }

        // 不匹配的指令直接返回原指令
        return originalInstr;
    }

    public static String getTypePackage(String type) {
        if (type.startsWith("L") && type.endsWith(";")) {
            String className = type.substring(1, type.length() - 1);
            int lastSlashIndex = className.lastIndexOf('/');
            if (lastSlashIndex != -1) {
                return className.substring(0, lastSlashIndex).replace('/', '.');
            }
        }
        return "";
    }

    public static void modifyFakerAppClass(List<ClassDef> aDexClassList) {
        // 遍历所有类查找目标类 Lcom/fakerandroid/boot/FakerApp
        for (int i = 0; i < aDexClassList.size(); i++) {
            ClassDef classDef = aDexClassList.get(i);
            if ("Lcom/fakerandroid/boot/FakerApp;".equals(classDef.getType())) {
                // 处理直接方法
                List<Method> modifiedDirectMethods = new ArrayList<>();
                for (Method method : classDef.getDirectMethods()) {
                    modifiedDirectMethods.add(modifyMethod(method));
                }

                // 处理虚拟方法
                List<Method> modifiedVirtualMethods = new ArrayList<>();
                for (Method method : classDef.getVirtualMethods()) {
                    modifiedVirtualMethods.add(modifyMethod(method));
                }

                // 创建修改后的类定义并替换原类
                ClassDef modifiedClass = new ImmutableClassDef(
                        classDef.getType(),
                        classDef.getAccessFlags(),
                        classDef.getSuperclass(),
                        classDef.getInterfaces(),
                        classDef.getSourceFile(),
                        classDef.getAnnotations(),
                        classDef.getStaticFields(),
                        classDef.getInstanceFields(),
                        modifiedDirectMethods,
                        modifiedVirtualMethods
                );
                aDexClassList.set(i, modifiedClass);
                break;
            }
        }
    }

    /**
     * 根据方法名统一处理需要修改的方法
     */
    private static Method modifyMethod(Method originalMethod) {
        String methodName = originalMethod.getName();
        String returnType = originalMethod.getReturnType();
        List<? extends MethodParameter> parameters = originalMethod.getParameters();

        // 检查方法签名是否匹配：方法名、返回值为void、无参数
        if ("V".equals(returnType) && parameters.isEmpty()) {
            switch (methodName) {
                case "onCreate":
                    return addInvokeToOnCreate(originalMethod);
                case "onLowMemory":
                    return addInvokeToOnLowMemory(originalMethod);
                case "onTerminate":
                    return addInvokeToOnTerminate(originalMethod);
            }
        }
        return originalMethod;
    }

    /**
     * 给onCreate方法添加SDK.init()调用
     * 正确指令：invoke-static {p0}, Lcom/jxsdk/api/SDK;->init(Landroid/app/Application;)V
     */
    private static Method addInvokeToOnCreate(Method originalMethod) {
        MethodImplementation impl = originalMethod.getImplementation();
        if (impl == null) return originalMethod;

        // 创建新的指令列表
        List<Instruction> instructions = new ArrayList<>();

        // 插入invoke-static指令（正确类型）
        instructions.add(new ImmutableInstruction35c(
                Opcode.INVOKE_STATIC,  // 使用静态方法调用指令
                1,  // 寄存器数量（只有p0一个参数）
                0,  // 寄存器p0（this）
                -1,
                -1,
                -1,
                -1,  // 未使用的寄存器
                new ImmutableMethodReference(
                        "Lcom/jxsdk/api/SDK;",
                        "init",
                        Collections.singletonList("Landroid/app/Application;"),  // 参数类型
                        "V"  // 返回值类型
                )
        ));

        // 添加原有的所有指令
        for (Instruction instr : impl.getInstructions()) {
            instructions.add(ImmutableInstruction.of(instr));
        }

        // 构建新的方法实例
        return new ImmutableMethod(
                originalMethod.getDefiningClass(),
                originalMethod.getName(),
                originalMethod.getParameters(),
                originalMethod.getReturnType(),
                originalMethod.getAccessFlags(),
                originalMethod.getAnnotations(),
                originalMethod.getHiddenApiRestrictions(),
                new ImmutableMethodImplementation(
                        impl.getRegisterCount(),
                        instructions,
                        impl.getTryBlocks(),
                        impl.getDebugItems()
                )
        );
    }

    /**
     * 给onLowMemory方法添加SDK.destroy()调用
     */
    private static Method addInvokeToOnLowMemory(Method originalMethod) {
        MethodImplementation impl = originalMethod.getImplementation();
        if (impl == null) return originalMethod;

        List<Instruction> instructions = new ArrayList<>();
        // 插入invoke-static指令（无参数）
        instructions.add(new ImmutableInstruction35c(
                Opcode.INVOKE_STATIC,
                0,  // 无寄存器参数
                -1, -1, -1, -1, -1,
                new ImmutableMethodReference(
                        "Lcom/jxsdk/api/SDK;",
                        "destroy",
                        Collections.emptyList(),  // 无参数
                        "V"
                )
        ));
        for (Instruction instr : impl.getInstructions()) {
            instructions.add(ImmutableInstruction.of(instr));
        }

        return new ImmutableMethod(
                originalMethod.getDefiningClass(),
                originalMethod.getName(),
                originalMethod.getParameters(),
                originalMethod.getReturnType(),
                originalMethod.getAccessFlags(),
                originalMethod.getAnnotations(),
                originalMethod.getHiddenApiRestrictions(),
                new ImmutableMethodImplementation(
                        impl.getRegisterCount(),
                        instructions,
                        impl.getTryBlocks(),
                        impl.getDebugItems()
                )
        );
    }

    /**
     * 给onTerminate方法添加SDK.destroy()调用
     */
    private static Method addInvokeToOnTerminate(Method originalMethod) {
        MethodImplementation impl = originalMethod.getImplementation();
        if (impl == null) return originalMethod;

        List<Instruction> instructions = new ArrayList<>();
        // 插入invoke-static指令（无参数）
        instructions.add(new ImmutableInstruction35c(
                Opcode.INVOKE_STATIC,
                0,  // 无寄存器参数
                -1, -1, -1, -1, -1,
                new ImmutableMethodReference(
                        "Lcom/jxsdk/api/SDK;",
                        "destroy",
                        Collections.emptyList(),  // 无参数
                        "V"
                )
        ));
        for (Instruction instr : impl.getInstructions()) {
            instructions.add(ImmutableInstruction.of(instr));
        }

        return new ImmutableMethod(
                originalMethod.getDefiningClass(),
                originalMethod.getName(),
                originalMethod.getParameters(),
                originalMethod.getReturnType(),
                originalMethod.getAccessFlags(),
                originalMethod.getAnnotations(),
                originalMethod.getHiddenApiRestrictions(),
                new ImmutableMethodImplementation(
                        impl.getRegisterCount(),
                        instructions,
                        impl.getTryBlocks(),
                        impl.getDebugItems()
                )
        );
    }

    public static void main(String[] args) throws Exception {
//        //替换smali文件里的字符串
//        Map<String, String> ids = new HashMap<>();
//        ids.put("0x7f0b001c", "0x7f000001");
//        replaceAndWrite(new File("C:/Users/15666/Desktop/test/MainActivity.smali"), ids);

        smali2Dex("C:\\Users\\Administrator\\Desktop\\dex\\smali","C:\\Users\\Administrator\\Desktop\\dex\\classes.dex");

//        dex2Smali("C:\\Users\\Administrator\\Desktop\\dex\\classes4.dex","C:\\Users\\Administrator\\Desktop\\dex\\smali");
    }
}
